home *** CD-ROM | disk | FTP | other *** search
/ Visual Basic Toolbox / Visual Basic Toolbox (P.I.E.)(1996).ISO / code_lib / objlibr / objlib12 / vboop2.txt < prev   
Text File  |  1995-04-26  |  16KB  |  398 lines

  1. #: 26702 S2/Beginner's Corner
  2.     21-Feb-95  14:50:28                              Thread=J20
  3. Sb: #26247-OO vb
  4. Fm: Kathleen Joeris 75330,156
  5. To: Pete Washburn 73750,3141
  6.  
  7. Pete,
  8.  
  9. Sorry I did not reply sooner; I was busy over the weekend.
  10.  
  11. (For anyone who wants to look at Pete's article: it is in the Beginner's
  12. Corner (sect 2) as VBOOP.ZIP.)
  13.  
  14. Pete: I do not understand one aspect of what you are doing.  This has to do
  15. with creating a new object of an inherited class.
  16.  
  17. First question is what hOBJ is passed when the NEW_OBJECT is called?
  18.  
  19. Second question, when does the pipeInstance and WaterpartInstance arrays get
  20. redimensioned?
  21.  
  22. Third question, what is the UDT for the pipe class?
  23.  
  24. Fourth question, is the following description of what happens when a new pipe
  25. is created correct?
  26.  
  27.   First, the pipeclass is called with a NEW_OBJECT message.
  28.  
  29.   Since this message is not handled by the pipeclass, it is passed to the
  30.   waterpartclass with the NEW_OBJECT message.
  31.  
  32.   I expected this to create a new element in the WaterpartInstances array
  33.   but I did not see a REDIM PRESERVE statement.  Did I miss something here?
  34.  
  35.   You then use the self function to call pipeclass, changing the message to
  36.   INIT_OBJECT
  37.  
  38.   INIT_OBJECT is not handled by the pipeclass, so this too is passed to the
  39.   waterpartclass
  40.  
  41.   You then use the self function to call pipeclass, changing the message to
  42.   SET_DIAMETER
  43.  
  44.   The pipeclass calls the WaterPartClass with the message SET_DIAMETER.
  45.   This is the deepest point of the stack, and the stack is
  46.  
  47.       WaterPart   (message = SET_DIAMETER)
  48.       Pipe        (message = SET_DIAMETER)
  49.       Self        (message = SET_DIAMETER)
  50.       WaterPart   (message = INIT_OBJECT)
  51.       Pipe        (message = INIT_OBJECT)
  52.       Self        (message = INIT_OBJECT)
  53.       WaterPart   (message = NEW_OBJECT)
  54.       Pipe        (message = NEW_OBJECT)
  55.  
  56.   The return value of Pipe(...SET_DIAMETER...) is an empty variant.  This
  57.   value is passed back, eventually to the calling routine.
  58.  
  59. I took a good bit of time with this.  I was unable to convince myself that it
  60. would work as stated.  Perhaps you can explain where I missed the boat. Have
  61. you actually used this code, or was it over simplified when you wrote this?
  62.  
  63. This seems to fully implement polymorphism, which in my mind is more important
  64. than inheritance.  I really like this aspect of your design.
  65.  
  66. You and Deborah hide the data within the function/module.  I can see the
  67. benefits of doing this, but do not do it that way.  Ultimately the speed of
  68. the seek is critical to this being practical. As I understand this design, you
  69. scan arrays eight times to perform a NEW_OBJECT.  This shows why I do not do
  70. it this way.
  71.  
  72. I believe that your inheritance would work.  I think I either misunderstood
  73. some things, or it needs some fixes to this code, but the underlying theory
  74. seems entirely sound. I understand why you go through the inheritance stack
  75. three times, but it is still a bit unwieldly.  This would particularly be a
  76. problem for a deeper inheritance stack.
  77.  
  78. I am very curious as to how you handle the inheritance of DATA.  You do not
  79. appear to do it in either of the manners I would have tried.  One would have
  80. been
  81.  
  82.    Type PipeClass
  83.      Parent as WaterPartClass
  84.      Length as Double
  85.    EndType
  86.  
  87. and the other would have been to disallow any access to waterpart data of the
  88. pipe object except through messages sent to the waterpart class.
  89.  
  90. THere are, as you mention, a number of things that could be done to make this
  91. more efficient. However, I think the basic theory is there in what you are
  92. doing.
  93.  
  94. Have a good day, Kathleen
  95.  
  96. =======================================================================
  97.  
  98. #: 375932 S0/Outbox File
  99.     22-Feb-95  17:32:00
  100. Sb: OO vb
  101. Fm: VBPJFO REP 26702
  102. To: Kathleen Joeris 75330,156
  103.  
  104. Hi Kathleen!
  105.  
  106. Boy, you sure dug in!  Good questions all.  Let me see if I can clarify them
  107. somewhat.
  108.  
  109.  
  110. >First question is what hOBJ is passed when the NEW_OBJECT is called?
  111.  
  112. I'm passing a Null when I create a new object as in:
  113.  
  114.        manifold1 = Pipe(Null, NEW_OBJECT, "2.5 100")
  115.  
  116. The first thing NEW_OBJECT does is checks the hObj passed to it.  If it is
  117. Null, then a new object is created (actually the object's handle).  The hObj
  118. is created by ObjectDirectory and returned from the function.  You
  119. then have a global handle to the object, in this example, manifold1
  120.  
  121.  
  122. >Second question, when does the pipeInstance and WaterpartInstance
  123. >arrays get redimensioned?
  124.  
  125. I'm showing my age and experience with QB 2.0, et. al.  The days before
  126. Redim Preserve!  Could very well use ReDim Preserve.  By the way, how fast
  127. is it? Of course, one of my other answers will deal with performance issues
  128. and I'm sure it won't be significant as new objects aren't created all that
  129. often.
  130.  
  131. In this example, the arrays are dimensioned during the INIT_CLASS method, as
  132. in:
  133.  
  134.        a = Pipe(Null, INIT_CLASS, 5)
  135.  
  136. In this case, I'm not planning on more than 5 instances of this class, so
  137. the instance arrays are dimmed to 5 elements.  No reason that you couldn't
  138. get rid of the INIT_CLASS method however and just Redim Preserve as
  139. necessary.  Much more dynamic that way too!!
  140.  
  141. >Third question, what is the UDT for the pipe class?
  142.  
  143. I'm sorry, I can't recall what "UDT" is.
  144.  
  145.  
  146. >Fourth question, is the following description of what happens when a new
  147. >pipe is created correct?
  148.  
  149. I don't have the actual file handy, but here's what I've got for NEW_OBJECT
  150. for the pipe class in my code here:
  151.  
  152.         Case NEW_OBJECT
  153.             ' get a new object handle if it hasn't already been created
  154.                 If IsNull(hObj) Then
  155.                     hObj = ObjectDirectory(Null, NEW_OBJECT, PIPE_CLASS)
  156.                 End If
  157.             ' create a new object instance in this class
  158.                 pipeCount = pipeCount + 1
  159.                 hInst = pipeCount
  160.                 objectInstanceHandles(hObj) = hInst
  161.             ' save the object handle in the instance
  162.                 pipeInstances(hInst).hObj = hObj
  163.             ' create the ancestor instances
  164.                 a = WaterPart(hObj, NEW_OBJECT, value)
  165.             ' initialize the object
  166.                 a = Pipe(hObj, INIT_OBJECT, parse(value))
  167.             ' return the object's handle
  168.                 Pipe = hObj
  169.  
  170.  
  171. Here's what's going on.  If an object handle hasn't been created by a
  172. descendant class, then the first thing done is to create one.  Next, the
  173. instance data is created.  To speed access to the instance data later, the
  174. instance's data array index is stored in another array indexed by the
  175. object's handle.  Then, the Pipe class passes the message on to the ancestor
  176. class, WaterPart in this case, so the WaterPart instances can be created for
  177. this object.  Finally, the object is initialized with INIT_OBJECT.
  178.  
  179. >  First, the pipeclass is called with a NEW_OBJECT message.
  180.  
  181. Yes
  182.  
  183. >  Since this message is not handled by the pipeclass, it is passed to the
  184. >  waterpartclass with the NEW_OBJECT message.
  185.  
  186. Yes it is handled by pipeClass and then passed on.  Sorry if the file you
  187. downloaded didn't have that.
  188.  
  189. >  I expected this to create a new element in the WaterPartInstances array
  190. >  but I did not see a REDIM PRESERVE statement.  Did I miss something here?
  191.  
  192. As discussed above, the PipeInstances array was dimmed earlier during
  193. INIT_CLASS in this example.  Could ReDim Preserve.
  194.  
  195. >  You then use the self function to call pipeclass, changing the message to
  196. >  INIT_OBJECT
  197.  
  198. Yes.  Now that all of the instance variables in the ancestor classes have
  199. been created, its safe to do all of the intializing that needs to be done.
  200. Here's the code for Pipe INIT_OBJECT:
  201.  
  202.         Case INIT_OBJECT
  203.             Pipe = Self(hObj, SET_LENGTH, value)
  204.  
  205. For this class, the only thing that has to be done is set the lenght of the
  206. pipe.  Self is used just in case a descendant class has another way of
  207. processing SET_LENGTH.  Note that when the object's handle hObj was created,
  208. one of the parameters to that call was the class of the object,  the
  209. PIPE_CLASS in this case.  When the object is created, the object's class is
  210. saved so that Self will know where to call to process any methods.  So if
  211. this object was a descendant of Pipe and had another SET_LENGTH method, it
  212. would be called instead of the Pipe class message.  If none of the
  213. descendant classes process the method, it will eventually get passed back
  214. here to the Pipe class.
  215.  
  216.  
  217. >  INIT_OBJECT is not handled by the pipeclass, so this too is passed to the
  218. >  waterpartclass
  219.  
  220. Yes it is.  See above.  (Sorry if this is different than in the file)  Note
  221. that each class's NEW_OBJECT calls INIT_OBJECT. This allows the instance
  222. variables for that class to be initialized.  So the WaterPart INIT_OBJECT is
  223. processed before control is returned back to Pipe NEW_OBJECT
  224.  
  225. (Continued)       
  226. S2
  227.  
  228.  
  229. #: 375938 S0/Outbox File
  230.     22-Feb-95  17:38:00
  231. Sb: OO vb
  232. Fm: VBPJFO REP 26702
  233. To: Kathleen Joeris 75330,156
  234.  
  235. (continued)
  236.  
  237. >  You then use the self function to call pipeclass, changing the message to
  238. >  SET_DIAMETER
  239.  
  240. >  The pipeclass calls the WaterPartClass with the message SET_DIAMETER.
  241. >  This is the deepest point of the stack, and the stack is
  242.  
  243. >      WaterPart   (message = SET_DIAMETER)
  244. >      Pipe        (message = SET_DIAMETER)
  245. >      Self        (message = SET_DIAMETER)
  246. >      WaterPart   (message = INIT_OBJECT)
  247. >      Pipe        (message = INIT_OBJECT)
  248. >      Self        (message = INIT_OBJECT)
  249. >      WaterPart   (message = NEW_OBJECT)
  250. >      Pipe        (message = NEW_OBJECT)
  251.  
  252.  
  253. I suspect my code has updated that somewhat.  Now, SET_DIAMETER is handled
  254. during WaterPart's INIT_OBJECT which is called from WaterPart's NEW_OBJECT.
  255. So the stack would be:
  256.  
  257.        WaterPart       (message = SET_DIAMETER)
  258.        Pipe            (message = SET_DIAMETER)
  259.        Self            (message = SET_DIAMETER)
  260.        WaterPart       (message = INIT_OBJECT)
  261.        WaterPart       (message = NEW_OBJECT)
  262.        Pipe            (message = NEW_OBJECT)
  263.  
  264. >  The return value of Pipe(...SET_DIAMETER...) is an empty variant.  This
  265. >  value is passed back, eventually to the calling routine.
  266.  
  267. In my working code, hObj get's passed back so you have a handle to the newly
  268. created object.  The Pipe function (class) does pass back a variant type so
  269. that I have the flexiblity to return basically whatever is needed back.
  270. Before the variant type existed in VB, I returned everything as a string
  271. which could be then converted into whatever data type was needed.  So even
  272. though I didn't catch on to the ReDim Preserve, I did see Variant as an
  273. enhancement to this OO thinking!
  274.  
  275. > I took a good bit of time with this.  I was unable to convince myself that
  276. > it would work as stated.  Perhaps you can explain where I missed the boat.
  277. > Have you actually used this code, or was it over simplified when you wrote
  278. > this?
  279.  
  280. The file consist of some messages I made several months ago (almost a year)
  281. During that time, the design has been enhanced and improved and hopefully
  282. with what I've added here, will make more sense.  There wasn't much interest
  283. in the thread back then, so I hadn't spent a lot of time keeping it current.
  284. The code I've shown today is actual code that seems to be doing quite well,
  285. although I'm always trying to improve things (who isn't!)
  286.  
  287.  
  288. > This seems to fully implement polymorphism, which in my mind is more
  289. > important than inheritance.  I really like this aspect of your design.
  290.  
  291. > You and Deborah hide the data within the function/module.  I can see the
  292. > benefits of doing this, but do not do it that way.  Ultimately the speed
  293. > of the seek is critical to this being practical. As I understand this
  294. > design, you scan arrays eight times to perform a NEW_OBJECT.  This shows
  295. > why I do not do it this way.
  296.  
  297. > I believe that your inheritance would work.  I think I either
  298. > misunderstood some things, or it needs some fixes to this code, but the
  299. > underlying theory seems entirely sound. I understand why you go through
  300. > the inheritance stack three times, but it is still a bit unwieldly.
  301. > This would particularly be a problem for a deeper inheritance stack.
  302.  
  303. I think this nearly completely encompasses the three major features of OO
  304. design.  Encapsulation, inheritance, and polymorphism.  VB certainly isn't
  305. optimized for this type of design, but you can meet most of the principles
  306. this way and receive 95% of the benefits of OO design.
  307.  
  308. I think the performance issues of any PURE OO design will always sacrifice
  309. speed for those benefits.  It certainly was true with Actor (a Smalltalk
  310. like language).  There sure is alot of message passing going on! I just
  311. don't see many ways to streamline the process without violating or losing
  312. some of the OO benefits.  If you don't hardcode in the object's class, you
  313. need to make all those calls to Self to allow any descendant class it's
  314. chance to process the method.  And if you don't pass any unhandled methods
  315. up to an ancestor, you're not gaining the inheritance benefits.  If you
  316. start opening up the instance data globally, then you've lost the benefits
  317. of encapsulation.
  318.  
  319. As always, life's a tradeoff.  In my applications, 99.9% of the time the
  320. computer is waiting for the user to click on something, so the small
  321. performance penalty is insignificant compared with the easy of designing and
  322. maintaining the program.  If this was a real time control program, then that
  323. equation would obviously change.  Although I've not played with it, I think
  324. C++ is a reasonable tradeoff between OO principles and performance issues.
  325. You can take those shortcuts when necessary for performance with the full
  326. understanding for what you're giving up.  With these techniques, I can do
  327. pretty much the same thing with VB.  That wasn't an option with Actor; you
  328. always had do to things in a pure OO way.
  329.  
  330. > I am very curious as to how you handle the inheritance of DATA.  You do
  331. > not appear to do it in either of the manners I would have tried.  One
  332. > would have been
  333.  
  334. >   Type PipeClass
  335. >     Parent as WaterPartClass
  336. >     Length as Double
  337. >   EndType
  338.  
  339. > and the other would have been to disallow any access to waterpart data of
  340. > the pipe object except through messages sent to the waterpart class.
  341.  
  342. This is one of the two major limitations of doing this with VB; there isn't
  343. an easy way to inherit the data from an ancestor class.  You could do it by
  344. including an instance variable of the ancestor's class in the class's
  345. instance variables as in:
  346.  
  347.     Type pipeObject
  348.         hObj As Integer
  349.        waterPart as WaterPartObject
  350.         length As Integer
  351.     End Type
  352.  
  353. But then you have the problem of keeping the contents of waterPart the same
  354. as the instance variables in WaterPart.  Plus if you get more than a couple
  355. of levels deep, it quickly becomes unmanageable.  So I had to accept almost
  356. pure encapsulation in each class, even from it's ancestors.  A pain in
  357. theory, but so far, hasn't been too much of a problem.
  358.  
  359. The other major limitation was the need to track the class of each object
  360. and indexing it's instance data in the various classes.  This is something
  361. that is automatic in the other OO languages I've seen.
  362.  
  363. > There are, as you mention, a number of things that could be done to make
  364. > this more efficient. However, I think the basic theory is there in what
  365. > you are doing.
  366.  
  367. Thanks.  It's always nice to be appreciated :)  Actually, I'm hopeing to
  368. gain some suggestions about improving it somewhat.  Your suggestion of ReDim
  369. Preserve fits into that category, thanks.
  370.  
  371. Pete Washburn,
  372. W.W. Programming
  373.  
  374.  
  375. #: 381305 S0/Outbox File
  376.     26-Feb-95  11:05:00
  377. Sb: OO vb
  378. Fm: VBPJFO REP 27047
  379. To: Kathleen Joeris [SL-1] 75330,156
  380.  
  381. Hi Kathleen,
  382.  
  383. UDT - User Defined Type.  I knew it looked familiar; just couldn't quite get
  384. a handle on it!   Here's the pipe object:
  385.  
  386. ' pipe objects
  387.     Type pipeObject
  388.         hObj As Integer
  389.         length As Integer               ' in feet
  390.         volume As Integer               ' in cubic feet
  391.         change As Integer               ' true/false
  392.     End Type
  393.  
  394.  
  395. Pete Washburn,
  396. W.W. Programming
  397.  
  398.